package jxtend.processor.eclipse.visitor.interceptor;

import jxtend.processor.eclipse.visitor.handler.impl.TypeUtils;
import jxtend.processor.reflect.ReflectMethod;
import jxtend.processor.reflect.ReflectMethod.ResultMethod1Args;
import jxtend.processor.reflect.ReflectMethod.ResultMethod3Args;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;

@SuppressWarnings("restriction")
public class FixNullTypeOfFieldBindingInterceptor extends ASTNodeVisitorInterceptorAdapter {

	private static final ResultMethod1Args<PackageBinding, Binding, char[]> getType0MethodOfPackageBinding =
			ReflectMethod.method(PackageBinding.class, Binding.class, "getType0", char[].class);
	
	private static final ResultMethod3Args<LookupEnvironment, ReferenceBinding, char[][], Boolean, Boolean>
		getTypeFromCompoundNameMethodOfLookupEnvironment = ReflectMethod.method(LookupEnvironment.class,
				ReferenceBinding.class, "getTypeFromCompoundName", char[][].class, boolean.class, boolean.class);
//	private ReferenceBinding getTypeFromCompoundName(char[][] compoundName, boolean isParameterized, boolean wasMissingType) {

	@Override
	public boolean interceptVisit(ASTNodeVisitorInterceptorContext interceptorChain, ASTNode node, Scope scope) {
		if (node instanceof FieldDeclaration && ((FieldDeclaration) node).binding != null
				&& ((FieldDeclaration) node).binding.type == null) {
			FieldDeclaration fieldDeclaration = (FieldDeclaration) node;

			char[][] memberTypeCompoundName = null;

			if (((FieldDeclaration) node).getKind() == AbstractVariableDeclaration.FIELD) {
				fieldDeclaration.binding.type = fieldDeclaration.type.resolveType((MethodScope) scope);

//				LookupEnvironment lookupEnvironment = scope.referenceCompilationUnit().scope.environment;
////
//////				ReferenceBinding declaringClass = TypeUtils.referenceContextBinding(scope.referenceContext());
//////				
//////				fieldDeclaration.binding.type = TypeUtils.createUnresolvedTypeBinding(
//////						scope, fieldDeclaration.type, declaringClass.typeVariables());
//				fieldDeclaration.binding.type = getTypeFromCompoundNameMethodOfLookupEnvironment.invoke(
//						lookupEnvironment, fieldDeclaration.type.getTypeName(),
//						(fieldDeclaration.type instanceof ParameterizedSingleTypeReference)
//							|| (fieldDeclaration.type instanceof ParameterizedQualifiedTypeReference),
//						false);
			} else {
				ReferenceBinding declaringClass = fieldDeclaration.binding.declaringClass;
				char[][] compoundName = declaringClass.compoundName;
				if (CharOperation.contains('$', compoundName[compoundName.length - 1])) {
					char[][] lastParts = CharOperation.splitOn('$', compoundName[compoundName.length - 1]);
					
					char[][] newCompoundName = new char[compoundName.length][]; // - 1 + lastParts.length
					System.arraycopy(compoundName, 0, newCompoundName, 0, compoundName.length - 1);
					newCompoundName[compoundName.length - 1] = lastParts[0];
//					for (int i = 0; i < lastParts.length; i++) {
//						newCompoundName[compoundName.length - 1 + i] = lastParts[i];
//					}
					compoundName = newCompoundName;

					memberTypeCompoundName = CharOperation.subarray(lastParts, 1, lastParts.length - 1);
				}
						
				fieldDeclaration.binding.type = scope.environment().getType(compoundName);
				if (memberTypeCompoundName != null) {
					Binding binding = getType0MethodOfPackageBinding.invoke(
							((SourceTypeBinding) fieldDeclaration.binding.type).fPackage,
							declaringClass.compoundName[declaringClass.compoundName.length - 1]);

					fieldDeclaration.binding.type = (TypeBinding) binding;
				}

				fieldDeclaration.binding.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
			}
//			System.out.println("### NEW methodDeclaration.binding.returnType: " + methodDeclaration.binding.returnType);
		}

		return super.interceptVisit(interceptorChain, node, scope);
	}
}
